package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.redis.RedisCache;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 *
 * @author ruoyi
 * @date 2025-01-17
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper,AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String,String> template;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IAlertDataService alertDataService;


    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule)?1:0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule)?1:0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids))?1:0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id)?1:0;
    }

    @Override
    public void deviceDataAlertJob() {
        //1.查询规则存在不（采用mybatisplus对规则表进行count计数处理）
        long count =this.count(new LambdaQueryWrapper<AlertRule>().eq(AlertRule::getStatus,1));
        if(count<1){
            return;
        }
        //2.查询所有设备上的数据//获取的是大value
        List<Object> values =template.opsForHash().values("iot:product_data_latest");
        if(CollUtil.isEmpty(values)){
            return;
        }

        List<DeviceData> deviceDataList=new ArrayList<>();//一个大集合，里面是一个又一个设备的对象集合，每个设备对象里面包含了每个设备对应的数据。嵌套结构为了之后进行设备对规则进行比对更方便
        values.forEach(value->{
            List<DeviceData> list = JSONUtil.toList(value.toString(), DeviceData.class);
            deviceDataList.addAll(list);
        });
        //3.遍历所有数据去匹配规则
        deviceDataList.forEach(deviceData -> {
            alertRuleData(deviceData);
        });
    }

    //数据匹配告警规则
    private void alertRuleData(DeviceData deviceData) {
        //1.查询所有的关联设备规则
        List<AlertRule> allAlertRules = this.list(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getIotId, -1)
                .eq(AlertRule::getStatus, 1));

        //2.查询某个设备规则
        List<AlertRule> iotDevicealertRules = this.list(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getIotId, deviceData.getIotId())
                .eq(AlertRule::getStatus, 1));

        //3.所有设备数据满足的规则放在一起
        allAlertRules.addAll(iotDevicealertRules);
        if (CollUtil.isEmpty(allAlertRules)) {
            return;
        }
        //4.将所有数据与规则进行匹配
        allAlertRules.forEach(alertRule -> {
            alertRuleMatchDeviceData(alertRule,deviceData);
        });
    }

    //遍历所有数据与规则进行一对一的匹配，判断是否进行告警行为
    private void alertRuleMatchDeviceData(AlertRule alertRule, DeviceData deviceData) {
        //1.判断是否在生效时间内(从alertRule中获取生效时间与deviceData中获取上报时间进行比对，判断是否在生效时间内)
        //将时间必须统一为相同的格式
        LocalTime time = deviceData.getAlarmTime().toLocalTime();
        //00:00:00-23:59:59
        String[] split = alertRule.getAlertEffectivePeriod().split("~");//将时间分成两部分
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        if (time.isBefore(startTime) || time.isAfter(endTime)) {
            return;
        }
        //2.校验数据是否达到阈值
        Double devicedataValue = Double.valueOf(deviceData.getDataValue());
        Double  ruleValue = alertRule.getValue();
        int compare = CompareUtil.compare(devicedataValue, ruleValue);
        if ((alertRule.getOperator().equals(">=") && compare >= 0) || (alertRule.getOperator().equals("<") && compare < 0)) {
            log.info("当前上报的数据符合规则异常");
        } else {
            //TODO //删除redis 的报警数
            template.delete("iot:duration:"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId());
            return;
        }
        //3.查询沉默周期
        String silent = template.opsForValue().get("iot:silent"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId());
        if (ObjectUtil.isNotEmpty(silent)) {
            return;
        }

        //4.判断持续周期（这里的在数据库中的duration就是字符串形式的数字，可以直接强转）
        String count = template.opsForValue().get("iot:duration:"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId());
        //redis 取次数--第一次算一次，不是就+1次
        int sum = count == null ? 1 : Integer.parseInt(count) + 1;
        if (!alertRule.getDuration().equals(sum)) {
            //为何不写成String.valueOf(sum）？
            template.opsForValue().set("iot:duration:"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId(), sum + "");
            return;
        }

        //5.所有规则都满足，我是不是要去告警
        template.delete("iot:duration:"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId());

        //6.添加沉默周期
        template.opsForValue().set("iot:silent"+deviceData.getIotId()+":"+deviceData.getFunctionId()+":"+alertRule.getId(), "111", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //7.校验完成
        List<Long> userIds = null;
        if (alertRule.getAlertDataType().equals(0)) { //等于0它是老人异常数据
            if (deviceData.getLocationType().equals(0)) { //老人的随身设备
                userIds = deviceMapper.selectNursingIdByIotIdWithElder(deviceData.getIotId());
            } else if (deviceData.getLocationType().equals(1) && deviceData.getPhysicalLocationType().equals(2)) { //老人固定设备--床
                userIds =deviceMapper.selectNursingIdByIotIdWithBed(deviceData.getIotId());
            }
        } else {
            //设备异常数据，找维修工，或者是行政人员
            userIds =deviceMapper.selectUserIdByRoleName("维修工");
        }
        //发送一份给超级管理员
        List<Long> managerIds = deviceMapper.selectUserIdByRoleName("行政人员");

        //去重
        Collection<Long> longs = CollUtil.addAll(userIds, managerIds);
        List<Long> idList = CollUtil.distinct(longs);

        //保存报警数据到数据库里
        insertBatchRuleData(idList,alertRule, deviceData);
    }
    /**
     * 保存报警数据
     *
     * @param idList
     * @param alertRule
     * @param deviceData
     */
    private void insertBatchRuleData(List<Long> idList, AlertRule alertRule, DeviceData deviceData) {
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        alertData.setAlertRuleId(alertRule.getId());
        alertData.setAlertReason(alertRule.getFunctionName()+alertRule.getOperator()+",持续"+alertRule.getValue()+"阈值"+alertRule.getDuration());
        alertData.setType(alertRule.getAlertDataType());
        alertData.setCreateTime(new Date());
        alertData.setUpdateTime(new Date());
        alertData.setStatus(0);
        alertData.setAccessLocation(deviceData.getAccessLocation());

        List<AlertData> alertDataList = new ArrayList<>();
        idList.forEach(id -> {
            AlertData  data = BeanUtil.toBean(alertData, AlertData.class);
            data.setId(null);
            data.setUserId(id);
            alertDataList.add(data);
        });
        alertDataService.saveBatch(alertDataList);



    }


}


